package com.util.concurrent.lock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author 宫静雨
 * @version 1.0
 * @since 2023-07-25 15:22:39
 */
public class AbcTest {

    private static final Integer process = Runtime.getRuntime().availableProcessors();

    public static void main(String[] args) throws InterruptedException {
        lc();
    }

    private static void obj() {
        Obj obj = new Obj();
        ExecutorService pool = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 3; i++) {
            pool.execute(obj::a);
            pool.execute(obj::c);
            pool.execute(obj::b);

            // TimeUnit.SECONDS.sleep(1);
        }

        pool.shutdown();
    }

    private static void lc() throws InterruptedException {
        Lc lc = new Lc();
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                process, process * 2, 1, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(process * 2),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );

        for (int i = 0; i < 3; i++) {
            pool.execute(lc::a);
            pool.execute(lc::c);
            pool.execute(lc::b);

            TimeUnit.SECONDS.sleep(1);
            Lc.round.incrementAndGet();
        }

        pool.shutdown();
    }

    private static class Obj {
        private static final Logger log = LoggerFactory.getLogger(Obj.class);
        private static final Object monitor = new Object();
        private static final AtomicInteger flag = new AtomicInteger(1);

        public void a() {
            synchronized (monitor) {
                while (flag.get() != 1) {
                    try {
                        monitor.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                log.info("a");
                flag.compareAndSet(1, 2);
                monitor.notifyAll();
            }
        }

        public void b() {
            synchronized (monitor) {
                while (flag.get() != 2) {
                    try {
                        monitor.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                log.info("b");
                flag.compareAndSet(2, 3);
                monitor.notifyAll();
            }
        }

        public void c() {
            synchronized (monitor) {
                while (flag.get() != 3) {
                    try {
                        monitor.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                log.info("c");
                flag.compareAndSet(3, 1);
                monitor.notifyAll();
            }
        }
    }

    private static class Ls {

    }

    private static class Lc {
        private static final Logger log = LoggerFactory.getLogger(Lc.class);
        private static final Lock LOCK = new ReentrantLock();
        private static final Condition c1 = LOCK.newCondition();
        private static final Condition c2 = LOCK.newCondition();
        private static final Condition c3 = LOCK.newCondition();
        private static final AtomicInteger flag = new AtomicInteger(1);
        public static final AtomicInteger round = new AtomicInteger(1);

        public void a() {
            LOCK.lock();
            try {
                while (flag.get() != 1) {
                    c1.await();
                }
                for (int i = 0; i < round.get(); i++) {
                    log.info("a");
                }
                flag.compareAndSet(1, 2);
                c2.signal();
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                LOCK.unlock();
            }
        }

        public void b() {
            LOCK.lock();
            try {
                while (flag.get() != 2) {
                    c2.await();
                }
                for (int i = 0; i < round.get(); i++) {
                    log.info("b");
                }
                flag.compareAndSet(2, 3);
                c3.signal();
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                LOCK.unlock();
            }
        }

        public void c() {
            LOCK.lock();
            try {
                while (flag.get() != 3) {
                    c3.await();
                }
                for (int i = 0; i < round.get(); i++) {
                    log.info("c");
                }
                flag.compareAndSet(3, 1);
                c1.signal();
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                LOCK.unlock();
            }
        }
    }

}
